import { autoToFixed } from "../utils";
import { GoldenRatioCalculator } from "../lib/quant/common/GoldenRatioCalculator";
import { Kline } from "../lib/quant";
import { MergeData } from "./DataHandler";
// import { outLogger } from "../common/logger";


/** 
 * 其他形式的symbol统一转成现货(各种数据只用现货作为唯一标识)
 * @example getSpotSymbol('BTCUSD_231229') -> BTCUSDT
 * */
export function getSpotSymbol(symbol: string) {
  const upperCaseSymbol = symbol.toUpperCase();
  const deliveryReg = /_(PERP|\d+)$/;
  if (deliveryReg.test(upperCaseSymbol)) {
    return upperCaseSymbol.replace(deliveryReg, 'T');
  }
  return upperCaseSymbol.includes("USDT") ? upperCaseSymbol : `${upperCaseSymbol}USDT`;
}

/**
 * 获取当前币
 * @example
 * parserBaseSymbol('BTCUSDT') -> 'BTC'
 * */
export function parserBaseSymbol(symbol: string) {
  const match = symbol.toUpperCase().match(/^(.*?)(USDT|BUSD|BTC|ETH|BNB)?$/i);
  return match ? match[1] : symbol;
}

/**
 * 获取对币
 * @example
 * parserQuoteSymbol('BTCUSDT') -> 'USDT'
 * */
export function parserQuoteSymbol(symbol: string) {
  const match = symbol.toUpperCase().match(/(USDT|BUSD|BTC|ETH|BNB)$/i);
  return match ? match[0] : symbol;
}

const goldenRatio = new GoldenRatioCalculator();
export function splitOrder({ currentPrice, prices, minVolume, balance }: { currentPrice: number, prices: number[], minVolume: number, balance: number }) {
  const target = arrAvg(prices);
  const splitVolumes = goldenRatio.calculate({ value: balance, min: minVolume });
  const isLong = currentPrice > target;
  const diff = Math.abs(currentPrice - target);
  const splitPrices = goldenRatio.calculate({ value: diff, min: currentPrice * 0.001 });
  const result = [];
  // outLogger.info(splitVolumes, splitPrices)
  let start = splitVolumes.length > splitPrices.length ? splitPrices.length : splitVolumes.length;
  for (const price of splitPrices) {
    const volume = splitVolumes[--start];
    if (!volume) {
      break;
    }
    result.push({
      price: isLong ? autoToFixed(target + price) : autoToFixed(target - price),
      volume: autoToFixed(volume),
    });
  }
  return result;
}

export function arrAvg(list: number[]) {
  const sum = list.reduce((pre, current) => pre + current, 0);
  return sum / list.length
}
/**
 * 相似度
 * @param current 
 * @param target 
 * @param toleranceRate 容差百分比
 */
export function isSimilarTo(current: number, target: number, toleranceRate: number): boolean {
  const isSameSign = Math.sign(current) === Math.sign(target);
  // 计算百分比阈值
  const threshold = Math.abs(current * toleranceRate);
  // 检查 a 和 b 之间的差异是否小于阈值
  return isSameSign && (Math.abs(current - target) <= threshold);
}


/**
 * 相似度(rsi)
 * @param current 
 * @param target 
 * @param toleranceRate 容差百分比
 */
export function isSimilarForRsi(current: number, target: number, toleranceRate: number): boolean {
  // 计算百分比阈值
  const threshold = Math.abs(current * toleranceRate);
  const better = target < 12 ? current < target : false;
  // 检查 a 和 b 之间的差异是否小于阈值
  return (Math.abs(current - target) <= threshold || better);
}

/**
 * 相似度(量比)
 * @param current 
 * @param target 
 * @param toleranceRate 容差百分比
 */
export function isSimilarForVolume(current: number, target: number, toleranceRate: number): boolean {
  // 计算百分比阈值
  const threshold = Math.abs(current * toleranceRate);
  const better = target > 2 && current > 2;
  // 检查 a 和 b 之间的差异是否小于阈值
  return (Math.abs(current - target) <= threshold) || better;
}

/** 计算上下影针 */
export function calculateShadowLengths(kline: Kline) {
  const upperShadow = kline.high - Math.max(kline.open, kline.close);
  const lowerShadow = Math.min(kline.open, kline.close) - kline.low;
  return { lowerShadow: lowerShadow, upperShadow: upperShadow };
}

export function isBollConverging(bands: { up: number; low: number }[]): boolean {
  const upDiff = bands[bands.length - 1].up - bands[0].up;
  const lowDiff = bands[bands.length - 1].low - bands[0].low;
  return upDiff < 0 && lowDiff > 0;
}
/**
 * 计算分数，越接近目标值，分数越接近100
 * @param num 
 * @param target 目标值
 * @returns 
 */
export function getScore(num: number, target: number): number {
  const delta = Math.abs(num - target);
  const score = 100 * Math.exp(-delta * delta / target);
  return num > target ? score * (target / num) : score * (num / target);
}

/** 获取0 - 100范围内的分值，越大分值越高 */
export function getDistanceScore(distance: number): number {
  const maxDistance = 100;
  const minDistance = 0;
  const maxScore = 100;
  const minScore = 0;

  const range = maxDistance - minDistance;
  const distanceRatio = (distance - minDistance) / range;
  const scoreRatio = 1 - distanceRatio;
  const score = scoreRatio * (maxScore - minScore) + minScore;

  return score;
}

/**
 * 余弦相似度
 * @param a 
 * @param b 
    const vectorA = [1, 2, 3];
    const vectorB = [4, 5, 6];
    const similarity = cosineSimilarity(vectorA, vectorB);
    console.log(`Cosine similarity: ${similarity.toFixed(4)}`);
 */
export function cosineSimilarity(a: number[], b: number[]): number {
  // 计算向量的点积  
  const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);

  // 计算向量的范数（模）  
  const normA = Math.sqrt(a.reduce((sum, val) => sum + val ** 2, 0));
  const normB = Math.sqrt(b.reduce((sum, val) => sum + val ** 2, 0));
  // if (isNaN(dotProduct / (normA * normB))) {
  //   console.log(dotProduct, normA, normB)
  // }
  // 如果任何一个向量的范数为0，则余弦相似度未定义，通常返回0  
  if (normA === 0 || normB === 0) {
    return 0;
  }

  // 计算余弦相似度  
  return dotProduct / (normA * normB);
}
// 示例K线数据
// const originalKLine = [100, 120, 80, 110, 90];
// const normalizedKLine = normalizeKLine(originalKLine);
// console.log("Normalized K-Line Data:", normalizedKLine);
export function normalizeKLine(kline: number[]): number[] {
  const min = Math.min(...kline);
  const max = Math.max(...kline);

  // 归一化处理
  const normalizedKLine = kline.map((value) => (value - min) / (max - min));

  return normalizedKLine;
}


export function findPeaksAndValleys(feature_vector_list: Kline[], currentIndex: number, {
  fluctuationTarget = 0.005
} = {} as {
  fluctuationTarget: number
}): 'peak' | 'valley' | void {
  const { close, high, low  } = feature_vector_list[currentIndex];
  const DIS_LEFT = -8;
  const DIS_RIGHT = 8;
  // 不存在
  if (!feature_vector_list[currentIndex + DIS_LEFT] || !feature_vector_list[currentIndex + DIS_RIGHT]) {
    return
  }
  const getklineAvg = (pre: Kline, cur: Kline) => {
    return {
      ...pre,
      high: (pre.high + cur.high) / 2,
      low: (pre.low + cur.low) / 2,
      close: (pre.close + cur.close) / 2,
      volume: (pre.volume + cur.volume) / 2,
    }
  }

  const left = feature_vector_list.slice(currentIndex + DIS_LEFT, currentIndex - 1).reduce(getklineAvg)
  const right = feature_vector_list.slice(currentIndex + 1, currentIndex + DIS_RIGHT).reduce(getklineAvg)
  let tempHigh = high;
  let tempLow = low;
  let tempopen: number;
  let tempclose: number;
  for (let i = currentIndex + DIS_LEFT; i < currentIndex + DIS_RIGHT; i++) {
    const cur = feature_vector_list[i];
    if (cur.high > tempHigh) {
      tempHigh = cur.high;
    } else if (cur.low < tempLow) {
      tempLow = cur.low;
    }
    if (tempopen === undefined) {
      tempopen = cur.open;
    }
    tempclose = cur.close;
  }

  const fluctuation =  tempclose > tempopen ? (tempHigh - tempLow) / tempLow : (tempHigh - tempLow) / tempHigh;

  if (fluctuation > fluctuationTarget) {
    // Check for a peak (top)
    if (high > left.high && high > right.high) {
      return 'peak'
    }
    // Check for a trough (bottom)
    else if (low < left.low && low < right.low) {
      return 'valley'
    }
  }
  return
}

// 延迟统计，初始化2，被调用2次后，返回true，需要create 实例化，需要有重置方法
export function createDelayCount(initCount: number) {
  let count = initCount;
  return {
    reset() {
      count = initCount;
    },
    trigger() {
      if (count <= 0) {
        return true;
      }
      count--;
      return false;
    },
    getCount() {
      return count;
    }
  }
}

export function calculateSlope(kLines: Kline[]): number | null {  
  // 转换时间戳和价格到数组  
  const prices = kLines.map(kLine => kLine.close > kLine.open ? kLine.high : kLine.low);  
  
  // 检查数据点数量是否足够进行拟合（至少两个点）  
  if (kLines.length < 2) {  
    return null; // 数据点不足，无法计算斜率  
  }  
  
  // 使用最小二乘法计算斜率  
  let sumX = 0;  
  let sumY = 0;  
  let sumXY = 0;  
  let sumXX = 0;  
  const n = kLines.length;  
  
  for (let i = 0; i < n; i++) {  
    sumX += i;  
    sumY += prices[i];  
    sumXY += i * prices[i];  
    sumXX += i * i;  
  }  
  
  // 计算斜率  
  const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);  
  
  return slope;  
}  

export function calculateSlopeSum(klines: Kline[]): number {  
  if (klines.length < 2) {  
    throw new Error("至少需要两个数据点来计算斜率总和");  
  }  
  
  let slopeSum = 0;  
  for (let i = 1; i < klines.length; i++) {  
    const prev = klines[i - 1];  
    const current = klines[i];  

    // 计算价格差  
    const priceDiff = current.close - prev.close;  
  
    // 计算斜率并累加到总和  
    const slope = priceDiff / prev.close;  
    slopeSum += slope;  
  }  
  
  return slopeSum;  
}  